package csvm_v2.transaction;

import csvm_v1.CSVM_V1;
import csvm_v1.exceptions.WrongCommandArgumentException;
import csvm_v1.products.Product;
import csvm_v2.CSVM_V2;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import utils.PriceUtility;

/**
 * Transaction adalah kelas yang mewakili proses satu session transaksi.
 * Transaction memiliki status transaksi yang mewakili posisi dari proses
 * transaksi
 *
 * @author Muqtafi Akhmad
 */
public class Transaction implements Runnable {

    @Override
    public void run() {
        start();
    }

    /**
     * enumerasi status transaksi
     */
    private static enum transactionStatus {

        init, input, select, end
    };
    /**
     * status transaksi
     */
    private transactionStatus status;
    /**
     * reference ke vending machine
     */
    private CSVM_V2 vmRef;
    /**
     * scanner untuk membaca command dari pengguna
     */
    private static Scanner in = new Scanner(System.in);
    /**
     * command yang dibaca dari pengguna
     */
    private static String command;

    /**
     * konstruktor dari Transaction
     *
     * @param vmRef reference ke vending machine
     */
    public Transaction(CSVM_V2 vmRef) {
        this.vmRef = vmRef;
    }

    /**
     * memulai sebuah transaksi
     */
    public void initTransaction() {
        vmRef.getCoinIn().emptyCoins();
        status = transactionStatus.init;
    }

    /**
     * melakukan transaksi produk dengan koin
     *
     * @param produk produk
     * @param jumlah jumlah produk
     */
    public void doTransaction(Product produk, Integer jumlah) {
        int totalMoney = vmRef.getCoinIn().getDimeCount() * PriceUtility.DIME_VALUE
                + vmRef.getCoinIn().getNickelCount() * PriceUtility.NICKEL_VALUE
                + vmRef.getCoinIn().getQuarterCount() * PriceUtility.QUARTER_VALUE;
        int lastProductCount = vmRef.getProductStock().getProductStock().get(produk);
        if (jumlah == 0 || lastProductCount == 0) {
            System.out.println("Return customer money: ");
            System.out.println("Total: " + totalMoney);
            vmRef.getCoinOut().giveChange(totalMoney, null);
        } else {
            totalMoney -= produk.getPrice() * jumlah;
            // salin coin dari coin receiver ke dalam coin container
            vmRef.getCoinStock().setDimeCount(vmRef.getCoinStock().getDimeCount() + vmRef.getCoinIn().getDimeCount());
            vmRef.getCoinStock().setNickelCount(vmRef.getCoinStock().getNickelCount() + vmRef.getCoinIn().getNickelCount());
            vmRef.getCoinStock().setQuarterCount(vmRef.getCoinStock().getQuarterCount() + vmRef.getCoinIn().getQuarterCount());
            if (vmRef.getCoinOut().giveChange(totalMoney, null)) {
                vmRef.getProductStock().giveProducts(produk, jumlah);
                vmRef.getProductOut().dispenseProduct(produk, jumlah);
            } else {
                System.out.println("Sorry, insuficient changes for you");
                totalMoney = vmRef.getCoinIn().getDimeCount() * PriceUtility.DIME_VALUE
                        + vmRef.getCoinIn().getNickelCount() * PriceUtility.NICKEL_VALUE
                        + vmRef.getCoinIn().getQuarterCount() * PriceUtility.QUARTER_VALUE;
                vmRef.getCoinOut().giveChange(totalMoney, null);
            }
        }
        vmRef.getCoinIn().emptyCoins();
    }

    /**
     * membeli produk, status dari transaksi dimulai dari init dan berakhir
     * ketika menjadi end
     *
     * @throws WrongCommandArgumentException
     */
    public void buyProduct() throws WrongCommandArgumentException {
        initTransaction();
        status = transactionStatus.input;
        while (status != transactionStatus.end) {
            System.out.print("\n>>");
            switch (status) {
                case input: {
                    boolean edges = true;
                    System.out.println("Silakan masukkan koin ke vending machine format : size,weight,thickness,isSerratedEdge");
                    command = in.nextLine();
                    // split, input coin
                    String inputArgs[] = command.split(",");
                    if (inputArgs.length != 4) {
                        throw (new WrongCommandArgumentException("Input koin salah"));
                    }
                    if (inputArgs[3].equals("false")) {
                        edges = false;
                    }
                    vmRef.getCoinIn().inputCoin(Float.parseFloat(inputArgs[0]), Float.parseFloat(inputArgs[1]), Float.parseFloat(inputArgs[2]), edges);
                    System.out.println("Pilihan : \n1. input koin lagi\n2. pilih produk");
                    command = in.nextLine();
                    int pilihan = Integer.parseInt(command);
                    if (pilihan == 1) {
                        // do nothing
                    } else if (pilihan == 2) {
                        // ubah status menjadi select
                        status = transactionStatus.select;
                    } else {
                        System.out.println("Pilihan tidak tersedia : " + pilihan);
                    }
                    break;
                }
                case select: {
                    System.out.println("Produk tersedia : ");
                    // print semua kode produk
                    Iterator it = vmRef.getCatalog().getCatalog().entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry pairs = (Map.Entry) it.next();
                        Product produk = (Product) pairs.getValue();
                        System.out.println(pairs.getKey() + ". " + produk.getProductName() + "(" + produk.getPrice() + ")");
                    }
                    System.out.println("Silakan masukkan kode pilihan produk : ");
                    command = in.nextLine();
                    Integer pilihan = Integer.parseInt(command);
                    if (vmRef.getCatalog().getCatalog().containsKey(pilihan)) {
                        Product produk = (Product) vmRef.getCatalog().getCatalog().get(pilihan);
                        System.out.println("Silakan masukkan jumlah produk : ");
                        command = in.nextLine();
                        Integer jumlah = Integer.parseInt(command);
                        Integer totalBayar = jumlah * produk.getPrice();
                        // cek apakah uang dalam box sudah memenuhi total bayar, jika tidak tanyakan apakah akan cancel pembelian?
                        if (totalBayar <= CSVM_V1.getUserCoinValue(vmRef.getCoinIn().getDimeCount(), vmRef.getCoinIn().getNickelCount(), vmRef.getCoinIn().getQuarterCount())) {
                            doTransaction(produk, jumlah);
                        } else {
                            System.out.println("Uang yang anda masukkan kurang, Apakah Anda akan menambahkan koin? y / n");
                            command = in.nextLine();
                            if (command.equals("y")) {
                                status = transactionStatus.input;
                            } else {
                                status = transactionStatus.end;
                            }
                        }
                    } else {
                        System.out.println("Produk dengan kode " + pilihan + " tidak tersedia");
                    }
                    break;
                }
                default: {
                    break;
                }
            }
        }
    }

    /**
     * memulai sebuah proses transaksi
     */
    public void start() {
        try {
            buyProduct();
        } catch (WrongCommandArgumentException ex) {
            Logger.getLogger(Transaction.class.getName()).log(Level.SEVERE, ex.getMessage());
        } finally {
            //jika terjadi kondisi dimana terjadi exception namun user telah berada pada state memilih produk
            //pastikan terjadi transaksi apapun yang terjadi
            if (((vmRef.getCoinIn().getDimeCount()) != 0 || (vmRef.getCoinIn().getNickelCount()) != 0 || (vmRef.getCoinIn().getQuarterCount()) != 0) && (status == transactionStatus.select)) {
                doTransaction(null, 0);
            }
        }
    }
}
